package com.anlu.alspringmvc.spring.framework.context;

import com.anlu.alspringmvc.spring.framework.annotation.ALAutowired;
import com.anlu.alspringmvc.spring.framework.annotation.ALController;
import com.anlu.alspringmvc.spring.framework.annotation.ALService;
import com.anlu.alspringmvc.spring.framework.beans.ALBeanWrapper;
import com.anlu.alspringmvc.spring.framework.beans.config.ALBeanDefinition;
import com.anlu.alspringmvc.spring.framework.beans.support.ALBeandefinitionReader;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 完成bean的创建和DI
 * @author Administrator
 */
public class ALApplicationContext {

    private ALBeandefinitionReader reader;
    /**
     * bean的容器
     */
    private Map<String, ALBeanDefinition> beanDefinitionMap = new HashMap<>();
    /**
     * bean实例的容器
     */
    private Map<String, ALBeanWrapper> factoryBeanInstanceCache = new HashMap<>();
    /**
     * factoryBean的工厂bean的容器
     */
    private Map<String,Object> factoryBeanObjectCache = new HashMap<>();


    public ALApplicationContext(String...configLocations) {
        //加载配置文件
        reader = new ALBeandefinitionReader(configLocations);
        try{
            //解析配置文件，封装成BeanDefinition
            List<ALBeanDefinition> beanDefinitionList = reader.loadBeanDefinitions();
            //3、把BeanDefintion缓存起来
            doRegistBeanDefinition(beanDefinitionList);

            doAutowrited();

        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private void doAutowrited() {
        //调用getBean()
        //这一步，所有的Bean并没有真正的实例化，还只是配置阶段
        for (Map.Entry<String,ALBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }

    private void doRegistBeanDefinition(List<ALBeanDefinition> beanDefinitionList)throws Exception{
        for(ALBeanDefinition beanDefinition : beanDefinitionList){
            if(this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists");
            }
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(),beanDefinition);
        }
    }

    /**
     * Bean的实例化，DI是从这个方法开始的
     * @param beanName
     * @return
     */
    public Object getBean(String beanName){
        //1. 先拿到BeanDefinition的配置信息
        ALBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        //2.反射实例化newInstace()
        Object instance = instantiateBean(beanName, beanDefinition);
        //3.封装成一个BeanWrapper
        ALBeanWrapper beanWrapper = new ALBeanWrapper(instance);
        //4.保存到IOC容器中
        factoryBeanInstanceCache.put(beanName,beanWrapper);
        //5.执行依赖注入
        populateBean(beanName,beanDefinition,beanWrapper);
        return beanWrapper.getWrapperInstance();
    }

    /**
     * 创建真正的实例对象
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object instantiateBean(String beanName,ALBeanDefinition beanDefinition){
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try{
            if(this.factoryBeanInstanceCache.containsKey(beanName)){
                instance = this.factoryBeanInstanceCache.get(beanName);
            }else{
                Class<?> clazz = Class.forName(className);
                //2.默认的类名首字母小写
                instance = clazz.newInstance();
                this.factoryBeanObjectCache.put(beanName, instance);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * DI 注入属性值
     * @param beanName bean名称
     * @param beanDefinition bean的定义类
     * @param beanWrapper bean的封装类型
     */
    private void populateBean(String beanName,ALBeanDefinition beanDefinition, ALBeanWrapper beanWrapper){
        //可能涉及到循环依赖？
        //A{ B b}
        //B{ A b}
        //用两个缓存，循环两次
        //1、把第一次读取结果为空的BeanDefinition存到第一个缓存
        //2、等第一次循环之后，第二次循环再检查第一次的缓存，再进行赋值
        //其实利用了三级缓存

        Object instance = beanWrapper.getWrapperInstance();
        Class<?> clazz = beanWrapper.getWrappedClass();

        //在spring中@Component 如果不是ALController 和 ALService 就返回
        if(!(clazz.isAnnotationPresent(ALController.class) || clazz.isAnnotationPresent(ALService.class))){
            return;
        }
        //把所有的包括private/proteced/default/public修饰的字段都取出来
        for(Field field:clazz.getDeclaredFields()){
            //如果字段脑袋上不是以 ALAutowired修饰的，就跳过
            if(!field.isAnnotationPresent(ALAutowired.class)){
                continue;
            }

            //获取到注解
            ALAutowired autowired = field.getAnnotation(ALAutowired.class);
            //如果用户没有自定义的beanName,就默认根据类型注入
            String autowiredBeanName = autowired.value().trim();
            if("".equals(autowiredBeanName)){
                //field.getType().getName() 获取字段的类型
                autowiredBeanName = field.getType().getName();
            }
            //暴力访问
            field.setAccessible(true);
            try{
                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
                    continue;
                }
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            }catch (IllegalAccessException e){
                e.printStackTrace();
                continue;
            }
        }

    }

    public Object getBean(Class beanClass){
        return getBean(beanClass.getName());
    }

    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }



}
